# Generate python code for standard int and float operators.

logical = [ 'and_', 'or_', 'xor' ]
int_only = [ 'lshift', 'rshift' ]
int_float = [ 'add', 'sub', 'mul', 'pow', 'mod', 'floordiv', 'truediv' ]
syms = { 'and_' : '&' , 'or_' : '|' , 'xor' : '^', 'lshift' : '<<',
         'rshift' : '>>', 'add' : '+', 'sub' : '-', 'mul' : '*', 'truediv' : '/',
         'floordiv' : '//', 'mod' : '%', 'pow' : '**', 'neg' : '-',
         'pos' : '+', 'invert' : '!' }
uns = [ 'neg', 'pos' ]
compare = [ ('eq', '=='), ('ne', '!='), ('lt', '<'),
            ('gt', '>'),  ('le', '<='),  ('ge', '>=')  ]

no_exceptions = logical + [c[0] for c in compare] + ['add', 'sub', 'mul' ]

def native_unary(op, t):
    print '@_no_raise'
    print '@_pure'
    print '''@c_function
def %s_%s(left:%s)->%s:
    pass''' % (op, t, t, t)
    print '%s.__%s__ = %s_%s\n' % (t, op, op, t)

def native_binary(op, left, right, result):
    print '@behaviour(_%s)' % op
    if op in no_exceptions:
        print '@_no_raise'
    print '@_pure'
    print '''@c_function
def %s_%s_%s(left:%s, right:%s)->%s:
    pass
'''% (op, left, right, left, right, result)

def bin_no_inplace(op):
    print '''@c_function
def no_%s(x, y):
    pass
''' % op
    print "_%s = make_binary_operator('%s', '%s', '%s', no_%s)" % (
            op, op, method(op), reverse(op), op)
    print 'del no_%s' % op
    print 'def %s(x, y):' % op
    print '   return x %s y' % syms[op]

def iop(op):
    if op[-1] == '_':
        return 'i' + op[:-1]
    else:
         return 'i' + op

def bin_multi(op):
    bin_no_inplace(op)
    print "_%s = make_operator('%s', '__%s__', _%s, 2)" % (iop(op), iop(op),
                                                        iop(op), op)
    print 'def %s(x, y):' % iop(op)
    print '   x %s= y' % syms[op]
    print '   return x'


def method(op):
    if op[-1] == '_':
        return '__%s_' % op
    else:
        return '__%s__' % op

def reverse(op):
    if op[-1] == '_':
        return '__r%s_' % op
    else:
        return '__r%s__' % op


print '''#This file is automatically generated

@c_function
def make_binary_operator(name:str, func_name:str, rfunc_name:str, fallback):
    pass

@c_function
def make_comparison_operator(name:str, func_name:str, rfunc_name:str, fallback):
    pass

@c_function
def make_operator(name:str, func_name:str, fallback, arity:int):
    pass

@_no_raise
@_pure
@c_function
def is_(a, b):
    pass

@_no_raise
@_pure
@c_function
def is_not(a, b):
    pass

@c_function
def unorderable_types(x, y):
    pass

_eq = make_comparison_operator('eq', '__eq__', '__eq__', is_)

_ne = make_comparison_operator('ne', '__ne__', '__ne__', is_not)

_lt = make_comparison_operator('lt', '__lt__', '__gt__', unorderable_types)

_le = make_comparison_operator('le', '__le__', '__ge__', unorderable_types)

_gt = make_comparison_operator('gt', '__gt__', '__lt__', unorderable_types)

_ge = make_comparison_operator('ge', '__ge__', '__le__', unorderable_types)
'''
for op in int_float:
    bin_multi(op)
for op in logical:
    bin_multi(op)
for op in int_only:
    bin_multi(op)

print """def no_getitem(x, y):
    raise TypeError("'%s' object is unsubscriptable" % x$type.__name__)

getitem = make_operator('getitem', '__getitem__', no_getitem, 2)
del no_getitem

def no_setitem(x, y, z):
    raise TypeError("'%s' object does not support item assignment" % x$type.__name__)

setitem = make_operator('setitem', '__setitem__', no_setitem, 3)
del no_setitem

@c_function
def add_binary_behaviour(self:_add$type, f, t = None):
    pass

_add$type.addbehaviour = add_binary_behaviour
del add_binary_behaviour

def behaviour(op):
    return op.addbehaviour

"""
for op in compare:
    native_binary(op[0], 'int', 'int', 'bool')
    native_binary(op[0], 'int', 'float', 'bool')
    native_binary(op[0], 'float', 'int', 'bool')
    native_binary(op[0], 'float', 'float', 'bool')
    native_binary(op[0], 'str', 'str', 'bool')

print ('''#Surrogate functions for operators
@c_function
def tracing_function(op, f):
    pass
''')

import sys
f = open(sys.argv[1])
for line in f:
    print line,

for op in int_float:
    native_binary(op, 'int', 'int', 'int')
    native_binary(op, 'int', 'float', 'float')
    native_binary(op, 'float', 'int', 'float')
    native_binary(op, 'float', 'float', 'float')

for op in logical:
    native_binary(op, 'bool', 'bool', 'bool')
    native_binary(op, 'int', 'int', 'int')
for op in int_only:
    native_binary(op, 'int', 'int', 'int')

native_unary('invert', 'int')
native_unary('invert', 'bool')
native_unary('pos', 'int')
native_unary('neg', 'int')
native_unary('pos', 'float')
native_unary('neg', 'float')

INPLACE_TEMPLATE = '''
@_globals_to_consts
@_no_trace
def surrogate_inplace_%(name)s(self, other):
    if self?__i%(name)s__:
        result = self$__i%(name)s__(other)
        if result is not NotImplemented:
            return result
    return self %(op)s other

tracing_function(_i%(name)s, surrogate_inplace_%(name)s)
del surrogate_inplace_%(name)s
'''

BIN_TEMPLATE = '''
@_globals_to_consts
@_no_trace
def surrogate_%(name)s(op1, op2):
    t1 = op1$type
    t2 = op2$type
    if t1 is not t2 and $subtype(t1, t2):
        if op2?__r%(name)s__:
            result = op2$__r%(name)s__(op1)
            if result is not NotImplemented:
                return result
        if op1?__%(name)s__:
            result = op1$__%(name)s__(op2)
            if result is not NotImplemented:
                return result
    else:
        if op1?__%(name)s__:
            result = op1$__%(name)s__(op2)
            if result is not NotImplemented:
                return result
        if op2?__r%(name)s__:
            result = op2$__r%(name)s__(op1)
            if result is not NotImplemented:
                return result
    raise TypeError("unsupported operand type(s) for %(op)s: '%%s' and '%%s'" %%
                    (t1$__name__, t2$__name__))

tracing_function(_%(opname)s, surrogate_%(name)s)
del surrogate_%(name)s

'''

COMP_TEMPLATE = '''
@_globals_to_consts
@_no_trace
def surrogate_%(name)s(op1, op2):
    t1 = op1$type
    t2 = op2$type
    if t1 is not t2 and $subtype(t1, t2):
        if op2?__%(rname)s__:
            result = op2$__%(rname)s__(op1)
            if result is not NotImplemented:
                return result
        if op1?__%(name)s__:
            result = op1$__%(name)s__(op2)
            if result is not NotImplemented:
                return result
    else:
        if op1?__%(name)s__:
            result = op1$__%(name)s__(op2)
            if result is not NotImplemented:
                return result
        if op2?__%(rname)s__:
            result = op2$__%(rname)s__(op1)
            if result is not NotImplemented:
                return result
    raise TypeError("unorderable type(s): '%%s' %(op)s '%%s'" %%
                    (t1$__name__, t2$__name__))

tracing_function(_%(name)s, surrogate_%(name)s)
del surrogate_%(name)s

'''

EQ_TEMPLATE = '''
@_globals_to_consts
@_no_trace
def surrogate_%(name)s(op1, op2):
    t1 = op1$type
    t2 = op2$type
    if t1 is not t2 and $subtype(t1, t2):
        if op2?__%(name)s__:
            result = op2$__%(name)s__(op1)
            if result is not NotImplemented:
                return result
        if op1?__%(name)s__:
            result = op1$__%(name)s__(op2)
            if result is not NotImplemented:
                return result
    else:
        if op1?__%(name)s__:
            result = op1$__%(name)s__(op2)
            if result is not NotImplemented:
                return result
        if op2?__%(name)s__:
            result = op2$__%(name)s__(op1)
            if result is not NotImplemented:
                return result
    return op1 is%(ne)s op2;

tracing_function(_%(name)s, surrogate_%(name)s)
del surrogate_%(name)s

'''

def print_surrogates(name, op, opname=None):
    if opname is None:
        opname = name
    print (INPLACE_TEMPLATE % dict(name=name, op=op, opname=opname))
    if op == '%':
        print (BIN_TEMPLATE % dict(name=name, op='%%', opname=opname))
    else:
        print (BIN_TEMPLATE % dict(name=name, op=op, opname=opname))

def print_comp_surrogates(name, op, rname):
    print (COMP_TEMPLATE % dict(name=name, op=op, rname=rname))

def print_eq_surrogates(name, op, ne):
    print (EQ_TEMPLATE % dict(name=name, op=op, ne=ne))

print_surrogates('add', '+')
print_surrogates('sub', '-')
print_surrogates('mod', '%')
print_surrogates('mul', '*')
print_surrogates('truediv', '/')
print_surrogates('floordiv', '//')
print_surrogates('lshift', '<<')
print_surrogates('rshift', '>>')
print_surrogates('pow', '**')
print_surrogates('and', '&', 'and_')
print_surrogates('or', '|', 'or_')
print_surrogates('xor', '^')

print_eq_surrogates('eq', '==', '')
print_eq_surrogates('ne', '!=', ' not')
print_comp_surrogates('lt', '<', 'gt')
print_comp_surrogates('gt', '>', 'lt')
print_comp_surrogates('le', '<=', 'ge')
print_comp_surrogates('ge', '>=', 'le')

print ('del tracing_function')
